package tech.aistar.corejava.exam.nio;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author success
 * @version 1.0
 * @description:本类用来演示:同步阻塞式IO，服务器实现模式为一个连接一个线程，
 * 即客户端有连接请求时服务器端就需要启动一个线程进行处理，
 * 如果这个连接不做任何事情会造成不必要的线程开销，当然可以通过线程池机制改善。
 * @date 2019/8/5 0005
 */
public class BioDemo {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1",8888);
            //获取输入流
            InputStream in = socket.getInputStream();
            Scanner sc = new Scanner(in);

            //遍历服务器端返回的结果,并且打印
            while(sc.hasNext()){
                System.out.println(sc.next());
            }

            sc.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/**阻塞IO处理多个客户端请求**/
class BioServerDemo{
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8888);

            //等待客户端连接
            Socket socket = serverSocket.accept();//此处会一直阻塞

            //处理请求
            OutputStream out = socket.getOutputStream();

            out.write("hello world\n".getBytes());

            out.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
/**阻塞IO处理多个客户端请求**/
class BioServerDemo2{
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8888);

            //等待客户端连接
            while(true) {
                Socket socket = serverSocket.accept();//此处会一直阻塞
                OutputStream out = socket.getOutputStream();

                out.write("hello world\n".getBytes());

                out.close();
            }
            //socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/**阻塞IO处理多个客户端请求
 * 这种写法有个非常严重的问题：
 由于整个接收请求和处理请求都是在同一个线程里（本示例是主线程）当处理客户端请求这一步发生了阻塞，或者说慢了，后来的所有连接请求都会被阻塞住。
 * **/
class BioServerDemo3{
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8888);

            //等待客户端连接
            while(true) {
                Socket socket = serverSocket.accept();//此处会一直阻塞
                new Thread(){
                    @Override
                    public void run() {
                        try {
                            OutputStream out = socket.getOutputStream();
                            out.write("hello world\n".getBytes());
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }
                }.start();
            }
            //socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 当时我没有说明一个非常重要的情况，假设一直没有客户端的连接进来，这一步就会阻塞住。而这完全是没有必要的，因为可能在一段时间，根本不会有客户端去连接服务端。我们希望的情况是，客户端有连接了，我们再去accept，打个比方，我再卖菜，我当然希望有人来买菜了，我才去收银。而不是，就在收银台那边干等着，浪费时间

 */
@SuppressWarnings("all")
class BioServerDemo4{
    public static void main(String[] args) throws IOException {
            ServerSocket serverSocket = new ServerSocket(8888);
            ExecutorService executorService = Executors.newCachedThreadPool();;

            //等待客户端连接
            while(true) {
                Socket socket = serverSocket.accept();//此处会一直阻塞
                System.out.println("======");

                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            OutputStream out = socket.getOutputStream();
                            out.write("hello world\n".getBytes());
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });

            }
    }
}
@SuppressWarnings("all")
class Bservler{
    public static void main(String[] args) throws Exception {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));
        serverSocketChannel.configureBlocking(false); //设置服务端操作都是非阻塞的

        Selector selector = Selector.open(); //选择器
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //对客户端的accept事件关心

        while (true) {
            selector.select(1000); //会阻塞住，直到有事件触发

            System.out.println("======aa===");

            Set<SelectionKey> selectionKeys = selector.selectedKeys(); //看下有哪些事件被触发了
            System.out.println("selectionKeys:" + selectionKeys);

            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();

                if (key.isAcceptable()) {
                    //客户端 accept被触发了
                    ServerSocketChannel serverChannel = (ServerSocketChannel)key.channel();

                    SocketChannel clientChannel = serverChannel.accept();
                    System.out.println("channel is acceptable");
                    clientChannel.configureBlocking(false);

                    //客户端channel注册OP_WRITE事件
                    clientChannel.register(selector, SelectionKey.OP_WRITE);
                } else if (key.isWritable()) {
                    //客户端可以往里写数据了

                    System.out.println("channel is writeable");
                    String data = "hello world\n";

                    //注意这里的是客户端的channel，因为是使用客户端channel注册OP_WRITE事件
                    SocketChannel clientChannel = (SocketChannel)key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(data.length());
                    buffer.put(data.getBytes());
                    buffer.flip();
                    clientChannel.write(buffer);
                }
                key.cancel(); //取消事件
                iterator.remove();
            }
        }
    }
}